﻿/**	vector.h
 *	----	Create Time Stamp:	--2006-06-19 23:34:16--
 *-----------------------------------------------------------------------------
 *		Mean	:
 *		Author	:Nick Shallery	(nicknide@gmail.com)
 *		Version	:1.0.21.25		(2009-09-20 23:52:10)
 *-----------------------------------------------------------------------------
**/	/*YOU HAVE ALL LEFT WITH THIS CODE WHILE YOU KEEP COMMENT IN EVERY COPY.*/

#if			!defined(__LOLIX__SOLA__VECTOR__NICK__K_IN_G__)
#define		__LOLIX__SOLA__VECTOR__NICK__K_IN_G__
#ifdef		__cplusplus

#if			!defined(__LOLIX__LOLI_X_CFG__K_IN_G__)
	#include	"../lolix_cfg.h"
#endif	//	!defined(__LOLIX__LOLI_X_CFG__K_IN_G__)

#if			!defined(__LOLIX__TOY__ALIGN__HPP__NICK__K_IN_G__)
	#include	"../toy/align.hpp"
#endif	//	!defined(__LOLIX__TOY__ALIGN__HPP__NICK__K_IN_G__)
#if			!defined(__LOLIX__TOY__REBIND_TYPE__K_IN_G__)
	#include	"../toy/rebind_type.hpp"
#endif	//	!defined(__LOLIX__TOY__REBIND_TYPE__K_IN_G__)

#include	<memory>


namespace lolix{
namespace sola{
	namespace _help
	{
		template<unsigned int dim, typename v_type>
		class vector_base
		{
		public:
			enum { dim_value = dim };
			typedef v_type value_type;

#if			LOLIX_CFG__IS_DEBUG
		protected:
			LOLIX_INLINE_CALL vector_base(void){}
#endif	//	LOLIX_CFG__IS_DEBUG
		};

		template<unsigned int dim, typename v_type>
		class matrix_base
		{
		public:
			enum { dim_value = dim };
			typedef v_type value_type;

#if			LOLIX_CFG__IS_DEBUG
		protected:
			LOLIX_INLINE_CALL matrix_base(void){}
#endif	//	LOLIX_CFG__IS_DEBUG
		};
	}
	template <unsigned int dim, typename value_type = float>class vector;
	template <unsigned int dim, typename value_type = float>class matrix;
}}


template<typename v_type>
class lolix::sola::vector<1, v_type>
	: public _help::vector_base<1, v_type>
{
	typedef vector<1, v_type> this_type;
	typedef this_type&			this_ref;
	typedef	const	this_type&	this_const_ref;
public:
	LOLIX_INLINE_CALL this_type(void)
	{ ; }
	LOLIX_INLINE_CALL this_type(v_type in_x): x(in_x){}

	//template<unsigned int other_dim>
	//this_type& operator=(const vector<other_dim, v_type>& o)
	//{ x = o.x; return *this; }

	//template<unsigned int other_dim>
	//this_ref operator += (const vector<other_dim, v_type>& o)
	//{ x += o.x; return *this; }
	//template<unsigned int other_dim>
	//this_ref operator -= (const vector<other_dim, v_type>& o)
	//{ x -= o.x; return *this; }


	LOLIX_INLINE_CALL this_ref operator += (const this_type o){ x += o.x; return *this; }
	LOLIX_INLINE_CALL this_ref operator -= (const this_type o){ x -= o.x; return *this; }
	LOLIX_INLINE_CALL this_type operator - (void)const{ return this_type(x*v_type(-1)); }
	LOLIX_INLINE_CALL const v_type dot(const this_type o)const{ return x*o.x; }
	LOLIX_INLINE_CALL this_ref operator*=(const matrix<1, v_type> mat)
	{
		x = this->dot(mat.x);
		return *this;
	}

	LOLIX_INLINE_CALL this_ref operator *= (v_type o){ x *= o; return *this; }
	LOLIX_INLINE_CALL this_ref operator /= (v_type o){ x /= o; return *this; }
	LOLIX_INLINE_CALL this_ref operator *= (this_const_ref o){ x *= o.x; return *this; }
	LOLIX_INLINE_CALL this_ref operator /= (this_const_ref o){ x /= o/x; return *this; }

	LOLIX_INLINE_CALL void normalize(void)const{x = v_type(1);}
	template<typename size_type>
	LOLIX_INLINE_CALL v_type& operator[](size_type idx){return *(&x + idx);}
	template<typename size_type>
	LOLIX_INLINE_CALL v_type operator[](size_type idx)const{return *(&x + idx);}

	v_type x;
};



template<typename v_type>
class lolix::sola::vector<2, v_type>
	: public _help::vector_base<2, v_type>
{
	typedef vector<2, v_type>	this_type;
	typedef this_type&			this_ref;
	typedef const this_type&	this_const_ref;
public:
	LOLIX_INLINE_CALL this_type(void){ ; }
	LOLIX_INLINE_CALL this_type(v_type in_x, v_type in_y): x(in_x), y(in_y){}
	LOLIX_INLINE_CALL this_ref operator += (this_const_ref v)
	{
		x += v.x;
		y += v.y;
		return *this;
	}
	LOLIX_INLINE_CALL const this_type operator + (this_const_ref v)const
	{
		this_type tmp(*this);
		return tmp-=v;
	}

	LOLIX_INLINE_CALL this_ref operator -= (this_const_ref v)
	{
		x -= v.x;
		y -= v.y;
		return *this;
	}
	LOLIX_INLINE_CALL const this_type operator - (this_const_ref v)const
	{
		this_type tmp(*this);
		return tmp -= v;
	}
	LOLIX_INLINE_CALL const this_type operator - (void)const
	{
		this_type tmp;
		const v_type v(-1);
		tmp[0] = x*v;
		tmp[1] = y*v;
		return tmp;
	}
	LOLIX_INLINE_CALL v_type dot(this_const_ref o)const
	{
		v_type tmp = 0.f;
		tmp+=x*o.x;
		tmp+=y*o.y;

		return tmp;
	}


	template <typename size_type>
	LOLIX_INLINE_CALL v_type& operator[](size_type index){ return reinterpret_cast<v_type*>(this)[index]; }
	template <typename size_type>
	LOLIX_INLINE_CALL const v_type operator[](size_type index)const{ return const_cast<this_type&>(*this)[index]; }
	LOLIX_INLINE_CALL void operator()(v_type v0, v_type v1){this->x = v0; this->y = v1;}
	LOLIX_INLINE_CALL this_ref operator*=(const matrix<2, v_type>& mat)
	{
		this_type tmp;
		tmp[0] = this->dot(mat.x);
		tmp[1] = this->dot(mat.y);
		return *this = tmp;
	}

	LOLIX_INLINE_CALL this_ref operator *= (v_type v)
	{
		x *= v;
		y *= v;
		return *this;
	}

	LOLIX_INLINE_CALL this_ref operator /= (v_type v){return (*this)*= v_type(1)/v;}
	LOLIX_INLINE_CALL this_ref operator *= (this_const_ref o)
	{
		x *= o.x;
		y *= o.y;
		return *this;
	}

	LOLIX_INLINE_CALL this_ref operator /= (this_const_ref o)
	{
		x /= o.x;
		y /= o.y;
		return *this;
	}

	LOLIX_INLINE_CALL const this_type operator *(v_type v)const
	{
		this_type tmp(*this);
		tmp*=v;
		return tmp;
	}

	LOLIX_INLINE_CALL const this_type operator /(v_type v)const
	{
		this_type tmp(*this);
		tmp/=v;
		return tmp;
	}

	LOLIX_INLINE_CALL const this_type operator *(this_const_ref o)const
	{
		this_type tmp(*this);
		return tmp*=o;
	}

	LOLIX_INLINE_CALL const this_type operator /(this_const_ref o)const
	{
		this_type tmp(*this);
		return tmp/=o;
	}

	LOLIX_INLINE_CALL void normalize(void)
	{
		const v_type tmp = v_type(1)/size();
		x *= tmp;
		y *= tmp;
	}

	LOLIX_INLINE_CALL v_type size(void)const{return sqrt(size_pow_2());}
	LOLIX_INLINE_CALL v_type size_p2(void)const{return x*x + y*y;}

	v_type x;
	v_type y;
};


template<typename v_type>
class lolix::sola::vector<3, v_type>
	: public _help::vector_base<3, v_type>
{
	typedef vector<3, v_type>	this_type;
	typedef this_type&			this_ref;
	typedef const this_type&	this_const_ref;
public:
	LOLIX_INLINE_CALL this_type(void){ ; }
	LOLIX_INLINE_CALL this_type(v_type in_x, v_type in_y, v_type in_z): x(in_x), y(in_y), z(in_z){}
	LOLIX_INLINE_CALL this_ref operator += (this_const_ref v)
	{
		x += v.x;
		y += v.y;
		z += v.z;
		return *this;
	}
	LOLIX_INLINE_CALL const this_type operator + (this_const_ref v)const
	{
		this_type tmp(*this);
		return tmp+=v;
	}

	LOLIX_INLINE_CALL this_ref operator -= (this_const_ref v)
	{
		x -= v.x;
		y -= v.y;
		z -= v.z;
		return *this;
	}

	LOLIX_INLINE_CALL const this_type operator - (this_const_ref v)const
	{
		this_type tmp(*this);
		return tmp -= v;
	}

	LOLIX_INLINE_CALL const this_type operator - (void)const
	{
		this_type tmp;
		const v_type v(-1);
		tmp[0] = x*v;
		tmp[1] = y*v;
		tmp[2] = z*v;
		return tmp;
	}

	LOLIX_INLINE_CALL v_type dot(this_const_ref o)const
	{
		v_type tmp = v_type();
		tmp+=x*o.x;
		tmp+=y*o.y;
		tmp+=z*o.z;

		return tmp;
	}

	template <typename size_type>
	LOLIX_INLINE_CALL v_type& operator[](size_type index){ return reinterpret_cast<v_type*>(this)[index]; }
	template <typename size_type>
	LOLIX_INLINE_CALL const v_type operator[](size_type index)const{ return const_cast<this_type&>(*this)[index]; }
	LOLIX_INLINE_CALL void operator()(v_type v0, v_type v1, v_type v3){this->x = v0; this->y = v1; this->z = v3;}
	LOLIX_INLINE_CALL this_ref operator*=(const matrix<3, v_type>& mat)
	{
		this_type tmp;
		tmp[0] = this->dot(mat.x);
		tmp[1] = this->dot(mat.y);
		tmp[2] = this->dot(mat.z);
		return *this = tmp;
	}

	LOLIX_INLINE_CALL this_ref operator *= (v_type v)
	{
		x *= v;
		y *= v;
		z *= v;
		return *this;
	}

	LOLIX_INLINE_CALL this_ref operator *= (this_const_ref o)
	{
		x *= o.x;
		y *= o.y;
		z *= o.z;
		return *this;
	}

	LOLIX_INLINE_CALL this_ref operator /= (this_const_ref o)
	{
		x /= o.x;
		y /= o.y;
		z /= o.z;
		return *this;
	}

	LOLIX_INLINE_CALL this_ref operator /= (v_type v)
	{
		v = v_type(1)/v;
		return (*this)*=v;
	}

	LOLIX_INLINE_CALL const this_type operator / (v_type v)const
	{
		this_type tmp(*this);
		return tmp /= v;
	}

	LOLIX_INLINE_CALL const this_type operator *(v_type v)const
	{
		this_type tmp(*this);
		tmp*=v;
		return tmp;
	}

	LOLIX_INLINE_CALL const this_type operator * (this_const_ref o)const
	{
		this_type tmp(*this);
		return tmp*=o;
	}

	LOLIX_INLINE_CALL const this_type cross(this_const_ref o)const
	{
		this_type tmp;
		tmp[0] = y*o[2] - z*o[1];
		tmp[1] = z*o[0] - x*o[2];
		tmp[2] = x*o[1] - y*o[0];
		return tmp;
	}

	LOLIX_INLINE_CALL this_ref cross_to(this_const_ref o)
	{
		this_type tmp(*this);
		x = tmp[1]*o[2] - tmp[2]*o[1];
		y = tmp[2]*o[0] - tmp[0]*o[2];
		z = tmp[0]*o[1] - tmp[1]*o[0];
		return *this;
	}

	LOLIX_INLINE_CALL void normalize(void)
	{
		v_type tmp = v_type(1)/size();

		x *= tmp;
		y *= tmp;
		z *= tmp;
	}

	LOLIX_INLINE_CALL v_type size(void)const{return sqrt(size_p2());}
	LOLIX_INLINE_CALL v_type size_p2(void)const{return x*x + y*y + z*z;}

	v_type x;
	v_type y;
	v_type z;
};



template<typename v_type>
class lolix::sola::vector<4, v_type>
	: public _help::vector_base<4, v_type>
{
	typedef vector<4, v_type>	this_type;
	typedef this_type&			this_ref;
	typedef const this_type&	this_const_ref;
public:
	LOLIX_INLINE_CALL this_type(void){ ; }
	LOLIX_INLINE_CALL this_type(v_type in_x, v_type in_y, v_type in_z): x(in_x), y(in_y), z(in_z), w(1){}
	LOLIX_INLINE_CALL this_type(v_type in_x, v_type in_y, v_type in_z, v_type in_w): x(in_x), y(in_y), z(in_z), w(in_w){}
	//template<unsigned int other_dim>
	//this_type(const vector<other_dim, v_type>& o)
	//{ *this = o; }
	//template<unsigned int other_dim>
	//this_type& operator=(const vector<other_dim, v_type>& o)
	//{ memcpy(this, &o, static_min(sizeof this_type, sizeof o)); return *this; }

	//template<unsigned int other_dim>
	//this_ref operator += (const vector<other_dim, v_type>& o)
	//{
	//	unsigned int i = 0;
	//	switch(static_min(4, other_dim)&3)
	//	{
	//		while(i < static_min(4, other_dim))
	//		{
	//	case 0:	(*this)[i] += o[i];++i;
	//	case 3:	(*this)[i] += o[i];++i;
	//	case 2:	(*this)[i] += o[i];++i;
	//	case 1:	(*this)[i] += o[i];++i;
	//		}
	//	}

	//	return *this;
	//}
	LOLIX_INLINE_CALL this_ref operator += (this_const_ref v)
	{
		x += v.x;
		y += v.y;
		z += v.z;
		w += v.w;
		return *this;
	}
	LOLIX_INLINE_CALL this_type operator - (const this_type& o)const
	{
		this_type tmp(*this);
		return tmp-=o;
	}

	//template<unsigned int other_dim>
	//this_ref operator -= (const vector<other_dim, v_type>& o)
	//{
	//	unsigned int i = 0;
	//	switch(static_min(4, other_dim)&3)
	//	{
	//		while(i < static_min(4, other_dim))
	//		{
	//	case 0:	(*this)[i] -= o[i];++i;
	//	case 3:	(*this)[i] -= o[i];++i;
	//	case 2:	(*this)[i] -= o[i];++i;
	//	case 1:	(*this)[i] -= o[i];++i;
	//		}
	//	}

	//	return *this;
	//}
	LOLIX_INLINE_CALL this_ref operator -= (this_const_ref v)
	{
		x -= v.x;
		y -= v.y;
		z -= v.z;
		w -= v.w;
		return *this;
	}
	LOLIX_INLINE_CALL this_type operator - (void)const
	{
		this_type tmp;
		tmp[0] = -x;
		tmp[1] = -y;
		tmp[2] = -z;
		tmp[3] = -w;
		return tmp;
	}

	LOLIX_INLINE_CALL v_type dot(this_const_ref o)
	{
		v_type tmp = 0.f;
		tmp+=x*o.x;
		tmp+=y*o.y;
		tmp+=z*o.z;
		tmp+=w*o.w;

		return tmp;
	}


	template <typename size_type>
	LOLIX_INLINE_CALL v_type& operator[](size_type index){ return reinterpret_cast<v_type*>(this)[index]; }
	template <typename size_type>
	LOLIX_INLINE_CALL const v_type operator[](size_type index)const{ return const_cast<this_type&>(*this)[index]; }
	LOLIX_INLINE_CALL void operator()(v_type v0, v_type v1, v_type v3){this->x = v0; this->y = v1; this->z = v3;}
	LOLIX_INLINE_CALL void operator()(v_type v0, v_type v1, v_type v3, v_type v4){this->x = v0; this->y = v1; this->z = v3; this->w = v4;}
	LOLIX_INLINE_CALL this_ref operator*=(const matrix<4, v_type>& mat)
	{
		this_type tmp;
		tmp[0] = this->dot(mat[0]);
		tmp[1] = this->dot(mat[1]);
		tmp[2] = this->dot(mat[2]);
		tmp[3] = this->dot(mat[3]);
		return *this = tmp;
	}

	LOLIX_INLINE_CALL this_ref operator *= (v_type v)
	{
		x *= v;
		y *= v;
		z *= v;
		w *= v;
		return *this;
	}

	LOLIX_INLINE_CALL this_ref operator *= (this_const_ref o)
	{
		x *= o.x;
		y *= o.y;
		z *= o.z;
		w *= o.w;
		return *this;
	}

	LOLIX_INLINE_CALL const this_type operator * (const matrix<4, v_type>&mat)const
	{
		this_type t(*this);
		return t*=mat;
	}

	LOLIX_INLINE_CALL const this_type operator * (this_const_ref o)
	{
		this_type tmp(*this);
		return tmp *= o;
	}

	LOLIX_INLINE_CALL void normalize(void)
	{
		const v_type tmp = v_type(1)/size();

		x *= tmp;
		y *= tmp;
		z *= tmp;
		w *= tmp;
	}

	LOLIX_INLINE_CALL const value_type size_p2(void)const{return x*x + y*y + z*z + w*w;}
	LOLIX_INLINE_CALL const value_type size(void)const{return sqrt(size_p2());}

	v_type x;
	v_type y;
	v_type z;
	v_type w;
};


namespace lolix{namespace sola{

	typedef vector<2, float>	vec2;
	typedef vector<3, float>	vec3;
	typedef vector<4, float>	vec4;

	typedef	vector<2, double>	dvec2;
	typedef	vector<3, double>	dvec3;
	typedef	vector<4, double>	dvec4;

	typedef	vector<2, int>		ivec2;
	typedef	vector<3, int>		ivec3;
	typedef	vector<4, int>		ivec4;

	vec3 operator /(vec3::value_type v, vec3 const& t);
}}

SYS_REGIST_TYPE(lolix::sola::vec2);
SYS_REGIST_TYPE(lolix::sola::vec3);
SYS_REGIST_TYPE(lolix::sola::vec4);

SYS_REGIST_TYPE(lolix::sola::dvec2);
SYS_REGIST_TYPE(lolix::sola::dvec3);
SYS_REGIST_TYPE(lolix::sola::dvec4);

#else	//	__cplusplus
#	error	Must Have C++ Complier Support
#endif	//	__cplusplus

#endif	//	!defined(__LOLIX__SOLA__VECTOR__NICK__K_IN_G__)

